'
' *  Authors:  Benton Stark
' * 
' *  Copyright (c) 2007-2012 Starksoft, LLC (http://www.starksoft.com) 
' *
' * Permission is hereby granted, free of charge, to any person obtaining a copy
' * of this software and associated documentation files (the "Software"), to deal
' * in the Software without restriction, including without limitation the rights
' * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
' * copies of the Software, and to permit persons to whom the Software is
' * furnished to do so, subject to the following conditions:
' * 
' * The above copyright notice and this permission notice shall be included in
' * all copies or substantial portions of the Software.
' * 
' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
' * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
' * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
' * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
' * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
' * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
' * THE SOFTWARE.
' * 
' 


Imports System.Collections.Generic
Imports System.Text
Imports System.Globalization

Namespace FTP
    ''' <summary>
    ''' FTP server commands.
    ''' </summary>
    Public Enum FtpCmd
        ''' <summary>
        ''' Unknown command issued.
        ''' </summary>
        Unknown
        ''' <summary>
        ''' The USER command.
        ''' </summary>
        User
        ''' <summary>
        ''' The PASS command.
        ''' </summary>
        Pass
        ''' <summary>
        ''' The MKD command.  Make new directory.
        ''' </summary>
        Mkd
        ''' <summary>
        ''' The RMD command.  Remove directory.
        ''' </summary>
        Rmd
        ''' <summary>
        ''' The RETR command.  Retrieve file.
        ''' </summary>
        Retr
        ''' <summary>
        ''' The PWD command.  Print working directory.
        ''' </summary>
        Pwd
        ''' <summary>
        ''' The SYST command.  System status.
        ''' </summary>
        Syst
        ''' <summary>
        ''' The CDUP command.  Change directory up.
        ''' </summary>
        Cdup
        ''' <summary>
        ''' The DELE command.  Delete file or directory.
        ''' </summary>
        Dele
        ''' <summary>
        ''' The TYPE command.  Transfer type.
        ''' </summary>
        Type
        ''' <summary>
        ''' The CWD command.  Change working directory.
        ''' </summary>
        Cwd
        ''' <summary>
        ''' The PORT command.  Data port.
        ''' </summary>
        Port
        ''' <summary>
        ''' The PASV command.  Passive port.
        ''' </summary>
        Pasv
        ''' <summary>
        ''' The STOR command.  Store file.
        ''' </summary>
        Stor
        ''' <summary>
        ''' The STOU command.  Store file unique.
        ''' </summary>
        Stou
        ''' <summary>
        ''' The APPE command.  Append file.
        ''' </summary>
        Appe
        ''' <summary>
        ''' The RNFR command.  Rename file from.
        ''' </summary>
        Rnfr
        ''' <summary>
        ''' The RFTO command.  Rename file to.
        ''' </summary>
        Rnto
        ''' <summary>
        ''' The ABOR command.  Abort current operation.
        ''' </summary>
        Abor
        ''' <summary>
        ''' The LIST command.  List files.
        ''' </summary>
        List
        ''' <summary>
        ''' The NLST command.  Namelist files.
        ''' </summary>
        Nlst
        ''' <summary>
        ''' The SITE command.  Site.
        ''' </summary>
        Site
        ''' <summary>
        ''' The STAT command.  Status.
        ''' </summary>
        Stat
        ''' <summary> 
        ''' The NOOP command.  No operation.
        ''' </summary>
        Noop
        ''' <summary>
        ''' The HELP command.  Help.
        ''' </summary>
        Help
        ''' <summary>
        ''' The ALLO command.  Allocate space.
        ''' </summary>
        Allo
        ''' <summary>
        ''' The QUIT command.  Quite session.
        ''' </summary>
        Quit
        ''' <summary>
        ''' The REST command.  Restart transfer.
        ''' </summary>
        Rest
        ''' <summary>
        ''' The AUTH command.  Initialize authentication.
        ''' </summary>
        Auth
        ''' <summary>
        ''' The PBSZ command.
        ''' </summary>
        Pbsz
        ''' <summary>
        ''' The PROT command.  Security protocol.
        ''' </summary>
        Prot
        ''' <summary>
        ''' The MODE command.  Data transfer mode.
        ''' </summary>
        Mode
        ''' <summary>
        ''' The MDTM command.  Month Day Time command.
        ''' </summary>
        Mdtm
        ''' <summary>
        ''' The SIZE command.  File size.
        ''' </summary>
        ''' <remarks>
        ''' This command retrieves the size of the file as stored on the FTP server.  Not all FTP servers
        ''' support this command.
        ''' </remarks>
        Size
        ''' <summary>
        ''' The FEAT command.  Supported features.
        ''' </summary>
        ''' <remarks>
        ''' This command gets a list of supported features from the FTP server.  The feature list may contain
        ''' extended commands in addition to proprietrary commands that are not defined in RFC documents.
        ''' </remarks>
        Feat
        ''' <summary>
        ''' The XCRC command.  CRC file accuracy testing.
        ''' </summary>
        ''' <remarks>
        ''' This is a non-standard command not supported by all FTP servers and not defined in RFC documents.
        ''' </remarks>
        Xcrc
        ''' <summary>
        ''' The XMD5 command.  MD5 file integrity hashing.
        ''' </summary>
        ''' <remarks>
        ''' This is a non-standard command not supported by all FTP servers and not defined in RFC documents.
        ''' </remarks>
        Xmd5
        ''' <summary>
        ''' The XSHA1 command.  SHA1 file integerity hashing.
        ''' </summary>
        ''' <remarks>
        ''' This is a non-standard command not supported by all FTP servers and not defined in RFC documents.
        ''' </remarks>
        Xsha1
        ''' <summary>
        ''' The XSHA256 command.  SHA-256 file integerity hashing.
        ''' </summary>
        ''' <remarks>
        ''' This is a non-standard command not supported by all FTP servers and not defined in RFC documents.
        ''' </remarks>
        Xsha256
        ''' <summary>
        ''' The XSHA512 command.  SHA-512 file integerity hashing.
        ''' </summary>
        ''' <remarks>
        ''' This is a non-standard command not supported by all FTP servers and not defined in RFC documents.
        ''' </remarks>
        Xsha512
        ''' <summary>
        ''' The EPSV command.  Extended passive command.
        ''' </summary>
        ''' <remarks>
        ''' The EPSV command is an extended PASV command that supports both IPv4 and IPv6 network addressing and is defined
        ''' in the RFC 2428 document.
        ''' </remarks>
        Epsv
        ''' <summary>
        ''' The EPRT command.  Extended port command.
        ''' </summary>
        ''' <remarks>
        ''' The EPRT command is an extended PORT command that supports both IPv4 and IPv6 network addressing and is defined
        ''' in the RFC 2428 document.
        ''' </remarks>
        Eprt
        ''' <summary>
        ''' The MFMT command. Modify File Modification Time command.
        ''' </summary>
        Mfmt
        ''' <summary>
        ''' The MFCT command. Modify File Creation Time command.
        ''' </summary>
        Mfct
        ''' <summary>
        ''' The OPTS command.  This command allows an FTP client to define a parameter that will be used by a subsequent command. 
        ''' </summary>
        Opts
        ''' <summary>
        ''' The HASH command.  This command is not supported by all FTP servers.
        ''' </summary>
        ''' <remarks>
        ''' This command is in the RFC draft phase and is used to generate file hashes on FTP server.  
        ''' More information can be found searching the document named
        ''' "File Transfer Protocol HASH Command for Cryptographic Hashes" draft-ietf-ftpext2-hash-03.
        ''' </remarks>
        Hash
        ''' <summary>
        ''' The RANG command.  This command is not supported by all FTP servers.
        ''' </summary>
        ''' <remarks>
        ''' This command is in the RFC draft phase and is used specify a byte range for partical file hashes.  
        ''' More information can be found searching the document named
        ''' "File Transfer Protocol HASH Command for Cryptographic Hashes" draft-ietf-ftpext2-hash-03.
        ''' </remarks>
        Rang
        ''' <summary>
        ''' The CLNT command.  This command is not supported by all FTP servers.
        ''' </summary>
        ''' <remarks>
        ''' The CLieNT command is a command whereby the FTP client can identify itself to the FTP server.
        ''' Many FTP servers ignore this command.  The ServU FTP server requires this command to be sent
        ''' prior to other important commands. 
        ''' </remarks>
        Clnt
        ''' <summary>
        ''' The MLST command.  This command is not supported by all FTP servers.
        ''' </summary>
        ''' <remarks>
        ''' The MLST and MLSD commands are intended to standardize the file and
        ''' directory information returned by the server-FTP process.  These
        ''' commands differ from the LIST command in that the format of the
        ''' replies is strictly defined although extensible.
        ''' </remarks>
        Mlst
        ''' <summary>
        ''' The MLSD command.  This command is not supported by all FTP servers.
        ''' </summary>
        ''' <remarks>
        ''' The MLST and MLSD commands are intended to standardize the file and
        ''' directory information returned by the server-FTP process.  These
        ''' commands differ from the LIST command in that the format of the
        ''' replies is strictly defined although extensible.
        ''' </remarks>
        Mlsd
    End Enum

    ''' <summary>
    ''' FTP request object which contains the command, arguments and text or an FTP request.
    ''' </summary>
    Public Class FtpRequest
        Private _command As FtpCmd
        Private _arguments As String()
        Private _text As String
        Private _encoding As Encoding

        ''' <summary>
        ''' Default constructor.
        ''' </summary>
        Public Sub New()
            _encoding = Encoding.UTF8
            _command = New FtpCmd()
            _text = String.Empty
        End Sub

        ''' <summary>
        ''' FTP request constructor.
        ''' </summary>
        ''' <param name="encoding">Text encoding object to use.</param>
        ''' <param name="command">FTP request command.</param>
        ''' <param name="arguments">Parameters for the request</param>
        Friend Sub New(encoding As Encoding, command As FtpCmd, ParamArray arguments As String())
            _encoding = encoding
            _command = command
            _arguments = arguments
            _text = BuildCommandText()
        End Sub

        ''' <summary>
        ''' FTP request constructor.
        ''' </summary>
        ''' <param name="encoding">Text encoding object to use.</param>
        ''' <param name="command">FTP request command.</param>
        Friend Sub New(encoding As Encoding, command As FtpCmd)
            Me.New(encoding, command, Nothing)
        End Sub

        ''' <summary>
        ''' Get the FTP command enumeration value.
        ''' </summary>
        Public ReadOnly Property Command() As FtpCmd
            Get
                Return _command
            End Get
        End Property

        ''' <summary>
        ''' Get the FTP command arguments (if any).
        ''' </summary>
        Public ReadOnly Property Arguments() As List(Of String)
            Get
                Return New List(Of String)(_arguments)
            End Get
        End Property

        ''' <summary>
        ''' Get the FTP command text with any arguments.
        ''' </summary>
        Public ReadOnly Property Text() As String
            Get
                Return _text
            End Get
        End Property

        ''' <summary>
        ''' Gets a boolean value indicating if the command is a file transfer or not.
        ''' </summary>
        Public ReadOnly Property IsFileTransfer() As Boolean
            Get
                Return ((_command = FtpCmd.Retr) OrElse (_command = FtpCmd.Stor) OrElse (_command = FtpCmd.Stou) OrElse (_command = FtpCmd.Appe))
            End Get
        End Property

        Friend Function BuildCommandText() As String
            Dim commandText As String = _command.ToString().ToUpper(CultureInfo.InvariantCulture)

            If _arguments Is Nothing Then
                Return commandText
            Else
                Dim builder As New StringBuilder()
                For Each arg As String In _arguments
                    builder.Append(arg)
                    builder.Append(" ")
                Next
                Dim argText As String = builder.ToString().TrimEnd()

                If _command = FtpCmd.Unknown Then
                    Return argText
                Else
                    Return [String].Format("{0} {1}", commandText, argText).TrimEnd()
                End If
            End If
        End Function

        Friend Function GetBytes() As Byte()
            Return _encoding.GetBytes([String].Format("{0}" & vbCr & vbLf, _text))
        End Function

        Friend ReadOnly Property HasHappyCodes() As Boolean
            Get
                Return If(GetHappyCodes().Length = 0, False, True)
            End Get
        End Property


        Friend Function GetHappyCodes() As FtpResponseCode()
            Select Case _command
                Case FtpCmd.Unknown
                    Return BuildResponseArray()
                Case FtpCmd.Allo
                    Return BuildResponseArray(FtpResponseCode.CommandOkay, FtpResponseCode.CommandNotImplementedSuperfluousAtThisSite)
                Case FtpCmd.User
                    Return BuildResponseArray(FtpResponseCode.UserNameOkayButNeedPassword, FtpResponseCode.ServiceReadyForNewUser, FtpResponseCode.UserLoggedIn)
                Case FtpCmd.Pass
                    Return BuildResponseArray(FtpResponseCode.UserLoggedIn, FtpResponseCode.ServiceReadyForNewUser, FtpResponseCode.NotLoggedIn)
                Case FtpCmd.Cwd
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Pwd
                    Return BuildResponseArray(FtpResponseCode.PathNameCreated)
                Case FtpCmd.Dele
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Abor
                    Return BuildResponseArray()
                Case FtpCmd.Mkd
                    Return BuildResponseArray(FtpResponseCode.PathNameCreated)
                Case FtpCmd.Rmd
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Help
                    Return BuildResponseArray(FtpResponseCode.SystemStatusOrHelpReply, FtpResponseCode.HelpMessage, FtpResponseCode.FileStatus)
                Case FtpCmd.Mdtm
                    Return BuildResponseArray(FtpResponseCode.FileStatus, FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Stat
                    Return BuildResponseArray(FtpResponseCode.SystemStatusOrHelpReply, FtpResponseCode.DirectoryStatus, FtpResponseCode.FileStatus)
                Case FtpCmd.Cdup
                    Return BuildResponseArray(FtpResponseCode.CommandOkay, FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Size
                    Return BuildResponseArray(FtpResponseCode.FileStatus)
                Case FtpCmd.Feat
                    Return BuildResponseArray(FtpResponseCode.SystemStatusOrHelpReply)
                Case FtpCmd.Syst
                    Return BuildResponseArray(FtpResponseCode.NameSystemType)
                Case FtpCmd.Rnfr
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionPendingFurtherInformation)
                Case FtpCmd.Rnto
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Noop
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.Site
                    Return BuildResponseArray(FtpResponseCode.CommandOkay, FtpResponseCode.CommandNotImplementedSuperfluousAtThisSite, FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Pasv
                    Return BuildResponseArray(FtpResponseCode.EnteringPassiveMode)
                Case FtpCmd.Port
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.Type
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.Rest
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionPendingFurtherInformation)
                Case FtpCmd.Mode
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.Quit
                    Return BuildResponseArray()
                Case FtpCmd.Auth
                    Return BuildResponseArray(FtpResponseCode.AuthenticationCommandOkay, FtpResponseCode.AuthenticationCommandOkaySecurityDataOptional)
                Case FtpCmd.Pbsz
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.Prot
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.List, FtpCmd.Nlst, FtpCmd.Mlsd
                    Return BuildResponseArray(FtpResponseCode.DataConnectionAlreadyOpenSoTransferStarting, FtpResponseCode.FileStatusOkaySoAboutToOpenDataConnection, FtpResponseCode.ClosingDataConnection, FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Appe, FtpCmd.Stor, FtpCmd.Stou, FtpCmd.Retr
                    Return BuildResponseArray(FtpResponseCode.DataConnectionAlreadyOpenSoTransferStarting, FtpResponseCode.FileStatusOkaySoAboutToOpenDataConnection, FtpResponseCode.ClosingDataConnection, FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Mlst
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Xcrc, FtpCmd.Xmd5, FtpCmd.Xsha1, FtpCmd.Xsha256, FtpCmd.Xsha512
                    Return BuildResponseArray(FtpResponseCode.RequestedFileActionOkayAndCompleted)
                Case FtpCmd.Epsv
                    Return BuildResponseArray()
                Case FtpCmd.Eprt
                    Return BuildResponseArray()
                Case FtpCmd.Mfmt
                    Return BuildResponseArray(FtpResponseCode.FileStatus)
                Case FtpCmd.Mfct
                    Return BuildResponseArray(FtpResponseCode.FileStatus)
                Case FtpCmd.Opts
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case FtpCmd.Hash
                    Return BuildResponseArray(FtpResponseCode.FileStatus)
                Case FtpCmd.Clnt
                    Return BuildResponseArray(FtpResponseCode.CommandOkay)
                Case Else
                    Throw New FtpException([String].Format("No response code(s) defined for FtpCmd {0}.", _command.ToString()))
            End Select
        End Function

        Private Function BuildResponseArray(ParamArray codes As FtpResponseCode()) As FtpResponseCode()
            Return codes
        End Function

    End Class
End Namespace
